#include "xmux.h"
#include <iostream>
extern "C" {
#include <libavformat/avformat.h>
}
using namespace std;
void PrintErr(int err);

#define CERR(err)                                                              \
  if (err != 0) {                                                              \
    PrintErr(err);                                                             \
    return 0;                                                                  \
  }
void XMux::set_src_video_time_base(AVRational *tb) {
  if (!tb)
    return;
  unique_lock<mutex> lck(mux_);
  if (src_video_time_base_ == nullptr) {
    src_video_time_base_ = new AVRational;
  }
  *src_video_time_base_ = *tb;
}

void XMux::set_src_audio_time_base(AVRational *tb) {
  if (!tb)
    return;
  unique_lock<mutex> lck(mux_);
  if (src_audio_time_base_ == nullptr) {
    src_audio_time_base_ = new AVRational;
  }
  *src_audio_time_base_ = *tb;
}

XMux::~XMux() {
  unique_lock<mutex> lck(mux_);
  if (src_audio_time_base_) {
    delete src_audio_time_base_;
    src_audio_time_base_ = nullptr;
  }
  if (src_video_time_base_) {
    delete src_video_time_base_;
    src_video_time_base_ = nullptr;
  }
}
AVFormatContext *XMux::Open(const char *url, AVCodecParameters *video_para,
                            AVCodecParameters *audio_para) {
  AVFormatContext *c = nullptr;
  auto re = avformat_alloc_output_context2(&c, nullptr, nullptr, url);
  if (re < 0) {
    char buf[1024] = {0};
    av_strerror(re, buf, sizeof(buf) - 1);
    cerr << buf << endl;
    return 0;
  }
  // 添加视频音频流
  if (video_para) {
    auto vs = avformat_new_stream(c, nullptr); // 视频流
    avcodec_parameters_copy(vs->codecpar, video_para);
  }
  if (audio_para) {
    auto as = avformat_new_stream(c, nullptr); // 音频流
    avcodec_parameters_copy(as->codecpar, audio_para);
  }
  // 打开io
  re = avio_open(&c->pb, url, AVIO_FLAG_WRITE);
  if (re < 0) {
    char buf[1024] = {0};
    av_strerror(re, buf, sizeof(buf) - 1);
    cerr << buf << endl;
    avformat_free_context(c);
    return 0;
  }
  av_dump_format(c, 0, url, 1);
  return c;
}


bool XMux::Write(AVPacket *pkt) {
  if (!pkt)
    return false;
  unique_lock<mutex> lock(mux_);
  if (!c_)
    return false;
  cout << pkt->pts << " " << flush;
  // 后期重构考虑通过duration计算
  if (pkt->pts == AV_NOPTS_VALUE) {
    pkt->pts = 0;
    pkt->dts = 0;
  }
  if (pkt->stream_index == video_index_) {
    if (this->begin_video_pts_ < 0) {
      this->begin_video_pts_ = pkt->pts;
        lock.unlock();
        RescaleTime(pkt, begin_video_pts_, src_video_time_base_);
        lock.lock();
    }

  } else if (pkt->stream_index == audio_index_) {
    if (this->begin_audio_pts_ < 0) {
      this->begin_audio_pts_ = pkt->pts;
 lock.unlock();
        RescaleTime(pkt, begin_audio_pts_, src_audio_time_base_);
        lock.lock();
    }
  }
  // 写入一帧数据，内部缓冲排序dts，通过pkt == NULL写入缓冲
  auto re = av_interleaved_write_frame(c_, pkt);
  CERR(re);
  return true;
}

bool XMux::WriteEnd() {
  unique_lock<mutex> lock(mux_);
  if (!c_)
    return false;
  //auto re = av_interleaved_write_frame(c_, nullptr); // 写入缓冲
  //CERR(re);
  int re = av_write_trailer(c_);
  CERR(re);
  return true;
}
bool XMux::WriteHead() {
  unique_lock<mutex> lock(mux_);
  if (!c_)
    return false;
//会改变timebase
  int re = avformat_write_header(c_, nullptr);
  if (re < 0) {
    char buf[1024] = {0};
    av_strerror(re, buf, sizeof(buf) - 1);
    cerr << buf << endl;
    return false;
  }
  av_dump_format(c_, 0, c_->url, 1);
  this->begin_video_pts_ = -1;
  this->begin_audio_pts_ = -1;
  return true;
}
